home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / p_view.c1 < prev    next >
Encoding:
Text File  |  2002-09-24  |  27.2 KB  |  1,089 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20.  
  21. #include "g_local.h"
  22. #include "m_player.h"
  23.  
  24.  
  25.  
  26. static    edict_t        *current_player;
  27. static    gclient_t    *current_client;
  28.  
  29. static    vec3_t    forward, right, up;
  30. float    xyspeed;
  31.  
  32. float    bobmove;
  33. int        bobcycle;        // odd cycles are right foot going forward
  34. float    bobfracsin;        // sin(bobfrac*M_PI)
  35.  
  36. /*
  37. ===============
  38. SV_CalcRoll
  39.  
  40. ===============
  41. */
  42. float SV_CalcRoll (vec3_t angles, vec3_t velocity)
  43. {
  44.     float    sign;
  45.     float    side;
  46.     float    value;
  47.     
  48.     side = DotProduct (velocity, right);
  49.     sign = side < 0 ? -1 : 1;
  50.     side = fabs(side);
  51.     
  52.     value = sv_rollangle->value;
  53.  
  54.     if (side < sv_rollspeed->value)
  55.         side = side * value / sv_rollspeed->value;
  56.     else
  57.         side = value;
  58.     
  59.     return side*sign;
  60.     
  61. }
  62.  
  63.  
  64. /*
  65. ===============
  66. P_DamageFeedback
  67.  
  68. Handles color blends and view kicks
  69. ===============
  70. */
  71. void P_DamageFeedback (edict_t *player)
  72. {
  73.     gclient_t    *client;
  74.     float    side;
  75.     float    realcount, count, kick;
  76.     vec3_t    v;
  77.     int        r, l;
  78.     static    vec3_t    power_color = {0.0, 1.0, 0.0};
  79.     static    vec3_t    acolor = {1.0, 1.0, 1.0};
  80.     static    vec3_t    bcolor = {1.0, 0.0, 0.0};
  81.  
  82.     client = player->client;
  83.  
  84.     // flash the backgrounds behind the status numbers
  85.     client->ps.stats[STAT_FLASHES] = 0;
  86.     if (client->damage_blood)
  87.         client->ps.stats[STAT_FLASHES] |= 1;
  88.     if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum))
  89.         client->ps.stats[STAT_FLASHES] |= 2;
  90.  
  91.     // total points of damage shot at the player this frame
  92.     count = (client->damage_blood + client->damage_armor + client->damage_parmor);
  93.     if (count == 0)
  94.         return;        // didn't take any damage
  95.  
  96.     // start a pain animation if still in the player model
  97.     if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255)
  98.     {
  99.         static int        i;
  100.  
  101.         client->anim_priority = ANIM_PAIN;
  102.         if (client->ps.pmove.pm_flags & PMF_DUCKED)
  103.         {
  104.             player->s.frame = FRAME_crpain1-1;
  105.             client->anim_end = FRAME_crpain4;
  106.         }
  107.         else
  108.         {
  109.             i = (i+1)%3;
  110.             switch (i)
  111.             {
  112.             case 0:
  113.                 player->s.frame = FRAME_pain101-1;
  114.                 client->anim_end = FRAME_pain104;
  115.                 break;
  116.             case 1:
  117.                 player->s.frame = FRAME_pain201-1;
  118.                 client->anim_end = FRAME_pain204;
  119.                 break;
  120.             case 2:
  121.                 player->s.frame = FRAME_pain301-1;
  122.                 client->anim_end = FRAME_pain304;
  123.                 break;
  124.             }
  125.         }
  126.     }
  127.  
  128.     realcount = count;
  129.     if (count < 10)
  130.         count = 10;    // always make a visible effect
  131.  
  132.     // play an apropriate pain sound
  133.     if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum))
  134.     {
  135.         r = 1 + (rand()&1);
  136.         player->pain_debounce_time = level.time + 0.7;
  137.         if (player->health < 25)
  138.             l = 25;
  139.         else if (player->health < 50)
  140.             l = 50;
  141.         else if (player->health < 75)
  142.             l = 75;
  143.         else
  144.             l = 100;
  145.         gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0);
  146.     }
  147.  
  148.     // the total alpha of the blend is always proportional to count
  149.     if (client->damage_alpha < 0)
  150.         client->damage_alpha = 0;
  151.     client->damage_alpha += count*0.01;
  152.     if (client->damage_alpha < 0.2)
  153.         client->damage_alpha = 0.2;
  154.     if (client->damage_alpha > 0.6)
  155.         client->damage_alpha = 0.6;        // don't go too saturated
  156.  
  157.     // the color of the blend will vary based on how much was absorbed
  158.     // by different armors
  159.     VectorClear (v);
  160.     if (client->damage_parmor)
  161.         VectorMA (v, (float)client->damage_parmor/realcount, power_color, v);
  162.     if (client->damage_armor)
  163.         VectorMA (v, (float)client->damage_armor/realcount,  acolor, v);
  164.     if (client->damage_blood)
  165.         VectorMA (v, (float)client->damage_blood/realcount,  bcolor, v);
  166.     VectorCopy (v, client->damage_blend);
  167.  
  168.  
  169.     //
  170.     // calculate view angle kicks
  171.     //
  172.     kick = abs(client->damage_knockback);
  173.     if (kick && player->health > 0)    // kick of 0 means no view adjust at all
  174.     {
  175.         kick = kick * 100 / player->health;
  176.  
  177.         if (kick < count*0.5)
  178.             kick = count*0.5;
  179.         if (kick > 50)
  180.             kick = 50;
  181.  
  182.         VectorSubtract (client->damage_from, player->s.origin, v);
  183.         VectorNormalize (v);
  184.         
  185.         side = DotProduct (v, right);
  186.         client->v_dmg_roll = kick*side*0.3;
  187.         
  188.         side = -DotProduct (v, forward);
  189.         client->v_dmg_pitch = kick*side*0.3;
  190.  
  191.         client->v_dmg_time = level.time + DAMAGE_TIME;
  192.     }
  193.  
  194.     //
  195.     // clear totals
  196.     //
  197.     client->damage_blood = 0;
  198.     client->damage_armor = 0;
  199.     client->damage_parmor = 0;
  200.     client->damage_knockback = 0;
  201. }
  202.  
  203.  
  204.  
  205.  
  206. /*
  207. ===============
  208. SV_CalcViewOffset
  209.  
  210. Auto pitching on slopes?
  211.  
  212.   fall from 128: 400 = 160000
  213.   fall from 256: 580 = 336400
  214.   fall from 384: 720 = 518400
  215.   fall from 512: 800 = 640000
  216.   fall from 640: 960 = 
  217.  
  218.   damage = deltavelocity*deltavelocity  * 0.0001
  219.  
  220. ===============
  221. */
  222. void SV_CalcViewOffset (edict_t *ent)
  223. {
  224.     float        *angles;
  225.     float        bob;
  226.     float        ratio;
  227.     float        delta;
  228.     vec3_t        v;
  229.  
  230.  
  231. //===================================
  232.  
  233.     // base angles
  234.     angles = ent->client->ps.kick_angles;
  235.  
  236.     // if dead, fix the angle and don't add any kick
  237.     if (ent->deadflag)
  238.     {
  239.         VectorClear (angles);
  240.  
  241.         ent->client->ps.viewangles[ROLL] = 40;
  242.         ent->client->ps.viewangles[PITCH] = -15;
  243.         ent->client->ps.viewangles[YAW] = ent->client->killer_yaw;
  244.     }
  245.     else
  246.     {
  247.         // add angles based on weapon kick
  248.  
  249.         VectorCopy (ent->client->kick_angles, angles);
  250.  
  251.         // add angles based on damage kick
  252.  
  253.         ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME;
  254.         if (ratio < 0)
  255.         {
  256.             ratio = 0;
  257.             ent->client->v_dmg_pitch = 0;
  258.             ent->client->v_dmg_roll = 0;
  259.         }
  260.         angles[PITCH] += ratio * ent->client->v_dmg_pitch;
  261.         angles[ROLL] += ratio * ent->client->v_dmg_roll;
  262.  
  263.         // add pitch based on fall kick
  264.  
  265.         ratio = (ent->client->fall_time - level.time) / FALL_TIME;
  266.         if (ratio < 0)
  267.             ratio = 0;
  268.         angles[PITCH] += ratio * ent->client->fall_value;
  269.  
  270.         // add angles based on velocity
  271.  
  272.         delta = DotProduct (ent->velocity, forward);
  273.         angles[PITCH] += delta*run_pitch->value;
  274.         
  275.         delta = DotProduct (ent->velocity, right);
  276.         angles[ROLL] += delta*run_roll->value;
  277.  
  278.         // add angles based on bob
  279.  
  280.         delta = bobfracsin * bob_pitch->value * xyspeed;
  281.         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  282.             delta *= 6;        // crouching
  283.         angles[PITCH] += delta;
  284.         delta = bobfracsin * bob_roll->value * xyspeed;
  285.         if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  286.             delta *= 6;        // crouching
  287.         if (bobcycle & 1)
  288.             delta = -delta;
  289.         angles[ROLL] += delta;
  290.     }
  291.  
  292. //===================================
  293.  
  294.     // base origin
  295.  
  296.     VectorClear (v);
  297.  
  298.     // add view height
  299.  
  300.     v[2] += ent->viewheight;
  301.  
  302.     // add fall height
  303.  
  304.     ratio = (ent->client->fall_time - level.time) / FALL_TIME;
  305.     if (ratio < 0)
  306.         ratio = 0;
  307.     v[2] -= ratio * ent->client->fall_value * 0.4;
  308.  
  309.     // add bob height
  310.  
  311.     bob = bobfracsin * xyspeed * bob_up->value;
  312.     if (bob > 6)
  313.         bob = 6;
  314.     //gi.DebugGraph (bob *2, 255);
  315.     v[2] += bob;
  316.  
  317.     // add kick offset
  318.  
  319.     VectorAdd (v, ent->client->kick_origin, v);
  320.  
  321.     // absolutely bound offsets
  322.     // so the view can never be outside the player box
  323.  
  324.     if (v[0] < -14)
  325.         v[0] = -14;
  326.     else if (v[0] > 14)
  327.         v[0] = 14;
  328.     if (v[1] < -14)
  329.         v[1] = -14;
  330.     else if (v[1] > 14)
  331.         v[1] = 14;
  332.     if (v[2] < -22)
  333.         v[2] = -22;
  334.     else if (v[2] > 30)
  335.         v[2] = 30;
  336.  
  337.     VectorCopy (v, ent->client->ps.viewoffset);
  338. }
  339.  
  340. /*
  341. ==============
  342. SV_CalcGunOffset
  343. ==============
  344. */
  345. void SV_CalcGunOffset (edict_t *ent)
  346. {
  347.     int        i;
  348.     float    delta;
  349.  
  350.     // gun angles from bobbing
  351.     ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005;
  352.     ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01;
  353.     if (bobcycle & 1)
  354.     {
  355.         ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL];
  356.         ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW];
  357.     }
  358.  
  359.     ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005;
  360.  
  361.     // gun angles from delta movement
  362.     for (i=0 ; i<3 ; i++)
  363.     {
  364.         delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i];
  365.         if (delta > 180)
  366.             delta -= 360;
  367.         if (delta < -180)
  368.             delta += 360;
  369.         if (delta > 45)
  370.             delta = 45;
  371.         if (delta < -45)
  372.             delta = -45;
  373.         if (i == YAW)
  374.             ent->client->ps.gunangles[ROLL] += 0.1*delta;
  375.         ent->client->ps.gunangles[i] += 0.2 * delta;
  376.     }
  377.  
  378.     // gun height
  379.     VectorClear (ent->client->ps.gunoffset);
  380. //    ent->ps->gunorigin[2] += bob;
  381.  
  382.     // gun_x / gun_y / gun_z are development tools
  383.     for (i=0 ; i<3 ; i++)
  384.     {
  385.         ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value);
  386.         ent->client->ps.gunoffset[i] += right[i]*gun_x->value;
  387.         ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value);
  388.     }
  389. }
  390.  
  391.  
  392. /*
  393. =============
  394. SV_AddBlend
  395. =============
  396. */
  397. void SV_AddBlend (float r, float g, float b, float a, float *v_blend)
  398. {
  399.     float    a2, a3;
  400.  
  401.     if (a <= 0)
  402.         return;
  403.     a2 = v_blend[3] + (1-v_blend[3])*a;    // new total alpha
  404.     a3 = v_blend[3]/a2;        // fraction of color from old
  405.  
  406.     v_blend[0] = v_blend[0]*a3 + r*(1-a3);
  407.     v_blend[1] = v_blend[1]*a3 + g*(1-a3);
  408.     v_blend[2] = v_blend[2]*a3 + b*(1-a3);
  409.     v_blend[3] = a2;
  410. }
  411.  
  412.  
  413. /*
  414. =============
  415. SV_CalcBlend
  416. =============
  417. */
  418. void SV_CalcBlend (edict_t *ent)
  419. {
  420.     int        contents;
  421.     vec3_t    vieworg;
  422.     int        remaining;
  423.  
  424.     ent->client->ps.blend[0] = ent->client->ps.blend[1] = 
  425.         ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0;
  426.  
  427.     // add for contents
  428.     VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg);
  429.     contents = gi.pointcontents (vieworg);
  430.     if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) )
  431.         ent->client->ps.rdflags |= RDF_UNDERWATER;
  432.     else
  433.         ent->client->ps.rdflags &= ~RDF_UNDERWATER;
  434.  
  435.     if (contents & (CONTENTS_SOLID|CONTENTS_LAVA))
  436.         SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend);
  437.     else if (contents & CONTENTS_SLIME)
  438.         SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend);
  439.     else if (contents & CONTENTS_WATER)
  440.         SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend);
  441.  
  442.     // add for powerups
  443.     if (ent->client->quad_framenum > level.framenum)
  444.     {
  445.         remaining = ent->client->quad_framenum - level.framenum;
  446.         if (remaining == 30)    // beginning to fade
  447.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0);
  448.         if (remaining > 30 || (remaining & 4) )
  449.             SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend);
  450.     }
  451.     else if (ent->client->invincible_framenum > level.framenum)
  452.     {
  453.         remaining = ent->client->invincible_framenum - level.framenum;
  454.         if (remaining == 30)    // beginning to fade
  455.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
  456.         if (remaining > 30 || (remaining & 4) )
  457.             SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend);
  458.     }
  459.     else if (ent->client->enviro_framenum > level.framenum)
  460.     {
  461.         remaining = ent->client->enviro_framenum - level.framenum;
  462.         if (remaining == 30)    // beginning to fade
  463.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
  464.         if (remaining > 30 || (remaining & 4) )
  465.             SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend);
  466.     }
  467.     else if (ent->client->breather_framenum > level.framenum)
  468.     {
  469.         remaining = ent->client->breather_framenum - level.framenum;
  470.         if (remaining == 30)    // beginning to fade
  471.             gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
  472.         if (remaining > 30 || (remaining & 4) )
  473.             SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend);
  474.     }
  475.  
  476.     // add for damage
  477.     if (ent->client->damage_alpha > 0)
  478.         SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1]
  479.         ,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend);
  480.  
  481.     if (ent->client->bonus_alpha > 0)
  482.         SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend);
  483.  
  484.     // drop the damage value
  485.     ent->client->damage_alpha -= 0.06;
  486.     if (ent->client->damage_alpha < 0)
  487.         ent->client->damage_alpha = 0;
  488.  
  489.     // drop the bonus value
  490.     ent->client->bonus_alpha -= 0.1;
  491.     if (ent->client->bonus_alpha < 0)
  492.         ent->client->bonus_alpha = 0;
  493. }
  494.  
  495.  
  496. /*
  497. =================
  498. P_FallingDamage
  499. =================
  500. */
  501. void P_FallingDamage (edict_t *ent)
  502. {
  503.     float    delta;
  504.     int        damage;
  505.     vec3_t    dir;
  506.  
  507.     if (ent->s.modelindex != 255)
  508.         return;        // not in the player model
  509.  
  510.     if (ent->movetype == MOVETYPE_NOCLIP)
  511.         return;
  512.  
  513.     if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity))
  514.     {
  515.         delta = ent->client->oldvelocity[2];
  516.     }
  517.     else
  518.     {
  519.         if (!ent->groundentity)
  520.             return;
  521.         delta = ent->velocity[2] - ent->client->oldvelocity[2];
  522.     }
  523.     delta = delta*delta * 0.0001;
  524.  
  525.     // never take falling damage if completely underwater
  526.     if (ent->waterlevel == 3)
  527.         return;
  528.     if (ent->waterlevel == 2)
  529.         delta *= 0.25;
  530.     if (ent->waterlevel == 1)
  531.         delta *= 0.5;
  532.  
  533.     if (delta < 1)
  534.         return;
  535.  
  536.     if (delta < 15)
  537.     {
  538.         ent->s.event = EV_FOOTSTEP;
  539.         return;
  540.     }
  541.  
  542.     ent->client->fall_value = delta*0.5;
  543.     if (ent->client->fall_value > 40)
  544.         ent->client->fall_value = 40;
  545.     ent->client->fall_time = level.time + FALL_TIME;
  546.  
  547.     if (delta > 30)
  548.     {
  549.         if (ent->health > 0)
  550.         {
  551.             if (delta >= 55)
  552.                 ent->s.event = EV_FALLFAR;
  553.             else
  554.                 ent->s.event = EV_FALL;
  555.         }
  556.         ent->pain_debounce_time = level.time;    // no normal pain sound
  557.         damage = (delta-30)/2;
  558.         if (damage < 1)
  559.             damage = 1;
  560.         VectorSet (dir, 0, 0, 1);
  561.  
  562.         if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) )
  563.             T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING);
  564.     }
  565.     else
  566.     {
  567.         ent->s.event = EV_FALLSHORT;
  568.         return;
  569.     }
  570. }
  571.  
  572.  
  573.  
  574. /*
  575. =============
  576. P_WorldEffects
  577. =============
  578. */
  579. void P_WorldEffects (void)
  580. {
  581.     qboolean    breather;
  582.     qboolean    envirosuit;
  583.     int            waterlevel, old_waterlevel;
  584.  
  585.     if (current_player->movetype == MOVETYPE_NOCLIP)
  586.     {
  587.         current_player->air_finished = level.time + 12;    // don't need air
  588.         return;
  589.     }
  590.  
  591.     waterlevel = current_player->waterlevel;
  592.     old_waterlevel = current_client->old_waterlevel;
  593.     current_client->old_waterlevel = waterlevel;
  594.  
  595.     breather = current_client->breather_framenum > level.framenum;
  596.     envirosuit = current_client->enviro_framenum > level.framenum;
  597.  
  598.     //
  599.     // if just entered a water volume, play a sound
  600.     //
  601.     if (!old_waterlevel && waterlevel)
  602.     {
  603.         PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  604.         if (current_player->watertype & CONTENTS_LAVA)
  605.             gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
  606.         else if (current_player->watertype & CONTENTS_SLIME)
  607.             gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
  608.         else if (current_player->watertype & CONTENTS_WATER)
  609.             gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
  610.         current_player->flags |= FL_INWATER;
  611.  
  612.         // clear damage_debounce, so the pain sound will play immediately
  613.         current_player->damage_debounce_time = level.time - 1;
  614.     }
  615.  
  616.     //
  617.     // if just completely exited a water volume, play a sound
  618.     //
  619.     if (old_waterlevel && ! waterlevel)
  620.     {
  621.         PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  622.         gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
  623.         current_player->flags &= ~FL_INWATER;
  624.     }
  625.  
  626.     //
  627.     // check for head just going under water
  628.     //
  629.     if (old_waterlevel != 3 && waterlevel == 3)
  630.     {
  631.         gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
  632.     }
  633.  
  634.     //
  635.     // check for head just coming out of water
  636.     //
  637.     if (old_waterlevel == 3 && waterlevel != 3)
  638.     {
  639.         if (current_player->air_finished < level.time)
  640.         {    // gasp for air
  641.             gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
  642.             PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  643.         }
  644.         else  if (current_player->air_finished < level.time + 11)
  645.         {    // just break surface
  646.             gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
  647.         }
  648.     }
  649.  
  650.     //
  651.     // check for drowning
  652.     //
  653.     if (waterlevel == 3)
  654.     {
  655.         // breather or envirosuit give air
  656.         if (breather || envirosuit)
  657.         {
  658.             current_player->air_finished = level.time + 10;
  659.  
  660.             if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0)
  661.             {
  662.                 if (!current_client->breather_sound)
  663.                     gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
  664.                 else
  665.                     gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
  666.                 current_client->breather_sound ^= 1;
  667.                 PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
  668.                 //FIXME: release a bubble?
  669.             }
  670.         }
  671.  
  672.         // if out of air, start drowning
  673.         if (current_player->air_finished < level.time)
  674.         {    // drown!
  675.             if (current_player->client->next_drown_time < level.time 
  676.                 && current_player->health > 0)
  677.             {
  678.                 current_player->client->next_drown_time = level.time + 1;
  679.  
  680.                 // take more damage the longer underwater
  681.                 current_player->dmg += 2;
  682.                 if (current_player->dmg > 15)
  683.                     current_player->dmg = 15;
  684.  
  685.                 // play a gurp sound instead of a normal pain sound
  686.                 if (current_player->health <= current_player->dmg)
  687.                     gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0);
  688.                 else if (rand()&1)
  689.                     gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
  690.                 else
  691.                     gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
  692.  
  693.                 current_player->pain_debounce_time = level.time;
  694.  
  695.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
  696.             }
  697.         }
  698.     }
  699.     else
  700.     {
  701.         current_player->air_finished = level.time + 12;
  702.         current_player->dmg = 2;
  703.     }
  704.  
  705.     //
  706.     // check for sizzle damage
  707.     //
  708.     if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
  709.     {
  710.         if (current_player->watertype & CONTENTS_LAVA)
  711.         {
  712.             if (current_player->health > 0
  713.                 && current_player->pain_debounce_time <= level.time
  714.                 && current_client->invincible_framenum < level.framenum)
  715.             {
  716.                 if (rand()&1)
  717.                     gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
  718.                 else
  719.                     gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
  720.                 current_player->pain_debounce_time = level.time + 1;
  721.             }
  722.  
  723.             if (envirosuit)    // take 1/3 damage with envirosuit
  724.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA);
  725.             else
  726.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA);
  727.         }
  728.  
  729.         if (current_player->watertype & CONTENTS_SLIME)
  730.         {
  731.             if (!envirosuit)
  732.             {    // no damage from slime with envirosuit
  733.                 T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME);
  734.             }
  735.         }
  736.     }
  737. }
  738.  
  739.  
  740. /*
  741. ===============
  742. G_SetClientEffects
  743. ===============
  744. */
  745. void G_SetClientEffects (edict_t *ent)
  746. {
  747.     int        pa_type;
  748.     int        remaining;
  749.  
  750.     ent->s.effects = 0;
  751.     ent->s.renderfx = 0;
  752.  
  753.     if (ent->health <= 0 || level.intermissiontime)
  754.         return;
  755.  
  756.     if (ent->powerarmor_time > level.time)
  757.     {
  758.         pa_type = PowerArmorType (ent);
  759.         if (pa_type == POWER_ARMOR_SCREEN)
  760.         {
  761.             ent->s.effects |= EF_POWERSCREEN;
  762.         }
  763.         else if (pa_type == POWER_ARMOR_SHIELD)
  764.         {
  765.             ent->s.effects |= EF_COLOR_SHELL;
  766.             ent->s.renderfx |= RF_SHELL_GREEN;
  767.         }
  768.     }
  769.  
  770.     if (ent->client->quad_framenum > level.framenum)
  771.     {
  772.         remaining = ent->client->quad_framenum - level.framenum;
  773.         if (remaining > 30 || (remaining & 4) )
  774.             ent->s.effects |= EF_QUAD;
  775.     }
  776.  
  777.     if (ent->client->invincible_framenum > level.framenum)
  778.     {
  779.         remaining = ent->client->invincible_framenum - level.framenum;
  780.         if (remaining > 30 || (remaining & 4) )
  781.             ent->s.effects |= EF_PENT;
  782.     }
  783.  
  784.     // show cheaters!!!
  785.     if (ent->flags & FL_GODMODE)
  786.     {
  787.         ent->s.effects |= EF_COLOR_SHELL;
  788.         ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
  789.     }
  790. }
  791.  
  792.  
  793. /*
  794. ===============
  795. G_SetClientEvent
  796. ===============
  797. */
  798. void G_SetClientEvent (edict_t *ent)
  799. {
  800.     if (ent->s.event)
  801.         return;
  802.  
  803.     if ( ent->groundentity && xyspeed > 225)
  804.     {
  805.         if ( (int)(current_client->bobtime+bobmove) != bobcycle )
  806.             ent->s.event = EV_FOOTSTEP;
  807.     }
  808. }
  809.  
  810. /*
  811. ===============
  812. G_SetClientSound
  813. ===============
  814. */
  815. void G_SetClientSound (edict_t *ent)
  816. {
  817.     char    *weap;
  818.  
  819.     if (ent->client->pers.game_helpchanged != game.helpchanged)
  820.     {
  821.         ent->client->pers.game_helpchanged = game.helpchanged;
  822.         ent->client->pers.helpchanged = 1;
  823.     }
  824.  
  825.     // help beep (no more than three times)
  826.     if (ent->client->pers.helpchanged && ent->client->pers.helpchanged <= 3 && !(level.framenum&63) )
  827.     {
  828.         ent->client->pers.helpchanged++;
  829.         gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
  830.         gi.centerprintf (ent, "Journal Entry - Press F1");
  831.     }
  832.  
  833.  
  834.     if (ent->client->pers.weapon)
  835.         weap = ent->client->pers.weapon->classname;
  836.     else
  837.         weap = "";
  838.  
  839.     if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
  840.         ent->s.sound = snd_fry;
  841.     else if (strcmp(weap, "weapon_railgun") == 0)
  842.         ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
  843.     else if (strcmp(weap, "weapon_bfg") == 0)
  844.         ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
  845.     else if (ent->client->weapon_sound)
  846.         ent->s.sound = ent->client->weapon_sound;
  847.     else
  848.         ent->s.sound = 0;
  849. }
  850.  
  851. /*
  852. ===============
  853. G_SetClientFrame
  854. ===============
  855. */
  856. void G_SetClientFrame (edict_t *ent)
  857. {
  858.     gclient_t    *client;
  859.     qboolean    duck, run;
  860.  
  861.     if (ent->s.modelindex != 255)
  862.         return;        // not in the player model
  863.  
  864.     client = ent->client;
  865.  
  866.     if (client->ps.pmove.pm_flags & PMF_DUCKED)
  867.         duck = true;
  868.     else
  869.         duck = false;
  870.     if (xyspeed)
  871.         run = true;
  872.     else
  873.         run = false;
  874.  
  875.     // check for stand/duck and stop/go transitions
  876.     if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
  877.         goto newanim;
  878.     if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
  879.         goto newanim;
  880.     if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
  881.         goto newanim;
  882.  
  883.     if(client->anim_priority == ANIM_REVERSE)
  884.     {
  885.         if(ent->s.frame > client->anim_end)
  886.         {
  887.             ent->s.frame--;
  888.             return;
  889.         }
  890.     }
  891.     else if (ent->s.frame < client->anim_end)
  892.     {    // continue an animation
  893.         ent->s.frame++;
  894.         return;
  895.     }
  896.  
  897.     if (client->anim_priority == ANIM_DEATH)
  898.         return;        // stay there
  899.     if (client->anim_priority == ANIM_JUMP)
  900.     {
  901.         if (!ent->groundentity)
  902.             return;        // stay there
  903.         ent->client->anim_priority = ANIM_WAVE;
  904.         ent->s.frame = FRAME_jump3;
  905.         ent->client->anim_end = FRAME_jump6;
  906.         return;
  907.     }
  908.  
  909. newanim:
  910.     // return to either a running or standing frame
  911.     client->anim_priority = ANIM_BASIC;
  912.     client->anim_duck = duck;
  913.     client->anim_run = run;
  914.  
  915.     if (!ent->groundentity)
  916.     {
  917.         client->anim_priority = ANIM_JUMP;
  918.         if (ent->s.frame != FRAME_jump2)
  919.             ent->s.frame = FRAME_jump1;
  920.         client->anim_end = FRAME_jump2;
  921.     }
  922.     else if (run)
  923.     {    // running
  924.         if (duck)
  925.         {
  926.             ent->s.frame = FRAME_crwalk1;
  927.             client->anim_end = FRAME_crwalk6;
  928.         }
  929.         else
  930.         {
  931.             ent->s.frame = FRAME_run1;
  932.             client->anim_end = FRAME_run6;
  933.         }
  934.     }
  935.     else
  936.     {    // standing
  937.         if (duck)
  938.         {
  939.             ent->s.frame = FRAME_crstnd01;
  940.             client->anim_end = FRAME_crstnd19;
  941.         }
  942.         else
  943.         {
  944.             ent->s.frame = FRAME_stand01;
  945.             client->anim_end = FRAME_stand40;
  946.         }
  947.     }
  948. }
  949.  
  950.  
  951. /*
  952. =================
  953. ClientEndServerFrame
  954.  
  955. Called for each player at the end of the server frame
  956. and right after spawning
  957. =================
  958. */
  959. void ClientEndServerFrame (edict_t *ent)
  960. {
  961.     float    bobtime;
  962.     int        i;
  963.  
  964.     current_player = ent;
  965.     current_client = ent->client;
  966.  
  967.     //
  968.     // If the origin or velocity have changed since ClientThink(),
  969.     // update the pmove values.  This will happen when the client
  970.     // is pushed by a bmodel or kicked by an explosion.
  971.     // 
  972.     // If it wasn't updated here, the view position would lag a frame
  973.     // behind the body position when pushed -- "sinking into plats"
  974.     //
  975.     for (i=0 ; i<3 ; i++)
  976.     {
  977.         current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
  978.         current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
  979.     }
  980.  
  981.     //
  982.     // If the end of unit layout is displayed, don't give
  983.     // the player any normal movement attributes
  984.     //
  985.     if (level.intermissiontime)
  986.     {
  987.         // FIXME: add view drifting here?
  988.         current_client->ps.blend[3] = 0;
  989.         current_client->ps.fov = 90;
  990.         G_SetStats (ent);
  991.         return;
  992.     }
  993.  
  994.     AngleVectors (ent->client->v_angle, forward, right, up);
  995.  
  996.     // burn from lava, etc
  997.     P_WorldEffects ();
  998.  
  999.     //
  1000.     // set model angles from view angles so other things in
  1001.     // the world can tell which direction you are looking
  1002.     //
  1003.     if (ent->client->v_angle[PITCH] > 180)
  1004.         ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
  1005.     else
  1006.         ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
  1007.     ent->s.angles[YAW] = ent->client->v_angle[YAW];
  1008.     ent->s.angles[ROLL] = 0;
  1009.     ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
  1010.  
  1011.     //
  1012.     // calculate speed and cycle to be used for
  1013.     // all cyclic walking effects
  1014.     //
  1015.     xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
  1016.  
  1017.     if (xyspeed < 5)
  1018.     {
  1019.         bobmove = 0;
  1020.         current_client->bobtime = 0;    // start at beginning of cycle again
  1021.     }
  1022.     else if (ent->groundentity)
  1023.     {    // so bobbing only cycles when on ground
  1024.         if (xyspeed > 210)
  1025.             bobmove = 0.25;
  1026.         else if (xyspeed > 100)
  1027.             bobmove = 0.125;
  1028.         else
  1029.             bobmove = 0.0625;
  1030.     }
  1031.     
  1032.     bobtime = (current_client->bobtime += bobmove);
  1033.  
  1034.     if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
  1035.         bobtime *= 4;
  1036.  
  1037.     bobcycle = (int)bobtime;
  1038.     bobfracsin = fabs(sin(bobtime*M_PI));
  1039.  
  1040.     // detect hitting the floor
  1041.     P_FallingDamage (ent);
  1042.  
  1043.     // apply all the damage taken this frame
  1044.     P_DamageFeedback (ent);
  1045.  
  1046.     // determine the view offsets
  1047.     SV_CalcViewOffset (ent);
  1048.  
  1049.     // determine the gun offsets
  1050.     SV_CalcGunOffset (ent);
  1051.  
  1052.     // determine the full screen color blend
  1053.     // must be after viewoffset, so eye contents can be
  1054.     // accurately determined
  1055.     // FIXME: with client prediction, the contents
  1056.     // should be determined by the client
  1057.     SV_CalcBlend (ent);
  1058.  
  1059.     // chase cam stuff
  1060.     if (ent->client->resp.spectator)
  1061.         G_SetSpectatorStats(ent);
  1062.     else
  1063.         G_SetStats (ent);
  1064.     G_CheckChaseStats(ent);
  1065.  
  1066.     G_SetClientEvent (ent);
  1067.  
  1068.     G_SetClientEffects (ent);
  1069.  
  1070.     G_SetClientSound (ent);
  1071.  
  1072.     G_SetClientFrame (ent);
  1073.  
  1074.     VectorCopy (ent->velocity, ent->client->oldvelocity);
  1075.     VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
  1076.  
  1077.     // clear weapon kicks
  1078.     VectorClear (ent->client->kick_origin);
  1079.     VectorClear (ent->client->kick_angles);
  1080.  
  1081.     // if the scoreboard is up, update it
  1082.     if (ent->client->showscores && !(level.framenum & 31) )
  1083.     {
  1084.         DeathmatchScoreboardMessage (ent, ent->enemy);
  1085.         gi.unicast (ent, false);
  1086.     }
  1087. }
  1088.  
  1089.